package com.stars.easyms.rest.handler;

import com.stars.easyms.base.constant.EasyMsCommonConstants;
import com.stars.easyms.base.constant.HttpHeaderConstants;
import com.stars.easyms.base.bean.EasyMsRequestEntity;
import com.stars.easyms.base.trace.EasyMsTraceSynchronizationManager;
import com.stars.easyms.base.util.*;
import com.stars.easyms.monitor.register.EasyMsMonitorRegister;
import com.stars.easyms.rest.constant.RestConstants;
import com.stars.easyms.rest.bean.EasyMsRestContext;
import com.stars.easyms.rest.exception.RestDuplicateRequestException;
import com.stars.easyms.rest.initializer.RequestMappingPathForRestInfo;
import com.stars.easyms.rest.manager.EasyMsRestRedisManager;
import com.stars.easyms.rest.properties.EasyMsRestProperties;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.lang.NonNull;

import javax.servlet.http.HttpServletRequest;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.TimeUnit;

import static com.stars.easyms.base.constant.EasyMsCommonConstants.ASYNC_ID_CONNECTION_SYMBOL;

/**
 * <p>className: EasyMsRequestHandler</p>
 * <p>description: EasyMs的请求处理器</p>
 *
 * @author guoguifang
 * @version 1.6.2
 * @date 2020/9/5 2:01 下午
 */
public class EasyMsRequestHandler {

    private static final Logger logger = LoggerFactory.getLogger(EasyMsRequestHandler.class);

    private EasyMsRestRedisManager easyMsRestRedisManager;

    private EasyMsRestProperties easyMsRestProperties;

    /**
     * 解析request头，不允许抛出异常
     */
    public void resolveRequest(@NonNull EasyMsRestContext easyMsRestContext) {

        // 获取request对象
        HttpServletRequest servletRequest = easyMsRestContext.getHttpServletRequest();

        // 获取接收请求时间
        long receiveRequestTime = System.currentTimeMillis();
        String receiveRequestTimeStr = DateTimeUtil.getDatetimeNormalStrWithMills(receiveRequestTime);

        // 获取请求地址
        String requestPath = servletRequest.getRequestURI().substring(servletRequest.getContextPath().length());

        // 校验请求地址，如果是easy-ms相关的url则直接返回null
        for (String permitUrl : HttpHeaderConstants.EASY_MS_URL) {
            if (PatternMatcherUtil.doUrlPatternMatch(permitUrl, requestPath, true)) {
                return;
            }
        }

        // 获取全局链路ID以及请求ID
        String traceId = servletRequest.getHeader(HttpHeaderConstants.TRACE_KEY);
        String requestId = servletRequest.getHeader(HttpHeaderConstants.HEADER_KEY_REQUEST_ID);
        String asyncId = servletRequest.getHeader(HttpHeaderConstants.HEADER_KEY_ASYNC_ID);
        if (asyncId != null) {
            asyncId = asyncId + ASYNC_ID_CONNECTION_SYMBOL + SpringBootUtil.getApplicationName();
        }

        // 获取请求系统，如果header头中不存在则使用unknown
        String requestSys = servletRequest.getHeader(HttpHeaderConstants.HEADER_KEY_REQUEST_SYS);
        if (StringUtils.isBlank(requestSys)) {
            requestSys = EasyMsCommonConstants.UNKNOWN;
        }

        // 获取header头中的请求时间
        String requestTime = servletRequest.getHeader(HttpHeaderConstants.HEADER_KEY_REQUEST_TIME);

        // 如果开启了请求去重则判断requestId是否已经存在
        if (easyMsRestRedisManager != null && easyMsRestProperties.isDuplicateRemovalEnabled() && StringUtils.isNotBlank(requestId)) {
            String redisKey = easyMsRestRedisManager.getRedisKeyWithPrefix(RestConstants.REDIS_DUPLICATE_REQUEST_KEY, requestPath, requestId);
            if (redisKey != null && !easyMsRestRedisManager.setIfAbsent(redisKey, receiveRequestTime, 10, TimeUnit.MINUTES)) {
                String firstRequestTimeStr = easyMsRestRedisManager.get(redisKey);
                if (StringUtils.isNotBlank(firstRequestTimeStr)) {
                    long firstRequestTime = Long.parseLong(firstRequestTimeStr);
                    throw new RestDuplicateRequestException("[捕获一个重复请求]-[请求地址:{}]-[请求系统:{}]-[服务系统:{}]-[链路ID:{}]-" +
                            "[请求ID:{}]{}-[请求时间:{}]-[首次接收请求时间:{}]-[接收请求时间:{}].",
                            requestPath, requestSys, SpringBootUtil.getApplicationName(), traceId, requestId,
                            TraceUtil.getAsyncIdTrace(asyncId), TraceUtil.withUnknown(requestTime),
                            DateTimeUtil.getDatetimeNormalStrWithMills(firstRequestTime), receiveRequestTimeStr);
                }
            }
        }

        // 如果是空的则设置一个默认值
        if (StringUtils.isBlank(traceId)) {
            traceId = TraceUtil.getTraceId();
        }
        if (StringUtils.isBlank(requestId)) {
            requestId = TraceUtil.getTraceId();
        }

        // 将traceId和requestId放入日志线程本地变量中
        EasyMsTraceSynchronizationManager.setTraceInfo(traceId, requestId);
        if (asyncId != null) {
            EasyMsTraceSynchronizationManager.setAsyncId(asyncId);
        }

        // 解析请求头中的用户信息
        String userInfoStr = servletRequest.getHeader(HttpHeaderConstants.HEADER_KEY_USER_INFO);
        String decodedUserInfoStr = userInfoStr;
        if (StringUtils.isNotBlank(userInfoStr)) {
            decodedUserInfoStr = new String(Base64.decodeBase64(userInfoStr), StandardCharsets.UTF_8);
            EasyMsTraceSynchronizationManager.setUserInfo(userInfoStr, decodedUserInfoStr);
        }

        // 记录接收服务日志信息
        if (isNotMonitorUrl(requestPath)) {
            logger.info("[接收服务-请求]-[请求地址:{}]-[请求系统:{}]-[服务系统:{}]-[请求ID:{}]{}-[请求时间:{}]-[接收请求时间:{}]{}.",
                    requestPath, requestSys, SpringBootUtil.getApplicationName(), requestId,
                    TraceUtil.getAsyncIdTrace(asyncId), TraceUtil.withUnknown(requestTime), receiveRequestTimeStr,
                    StringUtils.isNotBlank(decodedUserInfoStr) ? "-[请求用户信息:" + decodedUserInfoStr + "]" : "");
        }

        // 封装请求参数
        EasyMsRequestEntity requestEntity = new EasyMsRequestEntity();
        requestEntity.setRequestPath(requestPath);
        requestEntity.setTraceId(traceId);
        requestEntity.setRequestSys(requestSys);
        requestEntity.setRequestId(requestId);
        requestEntity.setAsyncId(asyncId);
        requestEntity.setRequestTime(requestTime);
        requestEntity.setReceiveRequestTime(receiveRequestTime);
        requestEntity.setReceiveRequestTimeStr(receiveRequestTimeStr);

        // 将requestEntity放入本地变量中
        EasyMsTraceSynchronizationManager.setRequestEntity(requestEntity);
        easyMsRestContext.setEasyMsRequestEntity(requestEntity);

        // 获取请求地址，根据请求地址获取rest信息，如果找不到对应rest信息则认为不是easy-ms-rest服务则跳过
        RequestMappingPathForRestInfo requestMappingPathForRestInfo = RestConstants.REQUEST_MAPPING_PATH_MAP.get(requestPath);
        if (requestMappingPathForRestInfo != null) {
            easyMsRestContext.setRequestMappingPathForRestInfo(requestMappingPathForRestInfo);
        }

        // 将rest上下文放入本地线程变量中
        EasyMsRestSynchronizationManager.setEasyMsRestContext(easyMsRestContext);
    }

    public EasyMsRequestHandler(EasyMsRestProperties easyMsRestProperties) {
        this.easyMsRestProperties = easyMsRestProperties;
        this.easyMsRestRedisManager = ApplicationContextHolder.getBean(EasyMsRestRedisManager.class);
    }

    private boolean isNotMonitorUrl(String url) {
        return !EasyMsMonitorRegister.getMonitorModuleNames().contains(url.substring(1));
    }

}
